#ifndef     __PWM_H
#define     __PWM_H

#include "./rcc.h"


namespace stm32f1::pwm {

#define TIMER_INSTANCE(BASE)        reinterpret_cast<TIM_TypeDef *>(BASE)

constexpr uint32_t __1M = 1000000;

template <uint32_t BASE, typename BUS, uint8_t CHAN>
struct PWMTimer : public BUS {

public:
    static inline void start_with_irq(uint32_t freq) {
        init();
        set_freq(freq);
        TIMER_INSTANCE(BASE)->DIER = TIM_DIER_UIE;
        start();
    }

    static inline void start_with_no_irq(uint32_t freq) {
        init();
        set_freq(freq);
        TIMER_INSTANCE(BASE)->DIER = 0;
        start();
    }

    static inline bool is_uie() {
        return TIMER_INSTANCE(BASE)->SR & TIM_SR_UIF;
    }

    static inline void clear_flag() {
        TIMER_INSTANCE(BASE)->SR &= ~TIM_SR_UIF;
    }

    static inline void stop() {
        TIMER_INSTANCE(BASE)->CR1 = 0x00;
        TIMER_INSTANCE(BASE)->DIER = 0x00;
    }

    static inline void set_freq(uint32_t freq) {
        TIMER_INSTANCE(BASE)->ARR = (__1M / freq) - 1;
        
        if constexpr (CHAN == 1) {
            TIMER_INSTANCE(BASE)->CCR1 = __1M / freq / 2;
        } else if constexpr (CHAN == 2) {
            TIMER_INSTANCE(BASE)->CCR2 = __1M / freq / 2;
        } else if constexpr (CHAN == 3) {
            TIMER_INSTANCE(BASE)->CCR3 = __1M / freq / 2;
        } else if constexpr (CHAN == 4) {
            TIMER_INSTANCE(BASE)->CCR4 = __1M / freq / 2;
        }
    }

private:
    static inline void start() {
        TIMER_INSTANCE(BASE)->EGR = TIM_EGR_UG;
        TIMER_INSTANCE(BASE)->CR1 = TIM_CR1_CEN;
    }

    static inline void init() {
        TIMER_INSTANCE(BASE)->CR1 = 0x00;
        TIMER_INSTANCE(BASE)->CR2 = 0x00;
        TIMER_INSTANCE(BASE)->PSC = 72 - 1;
         
        if constexpr (CHAN == 1) {
            TIMER_INSTANCE(BASE)->CCMR1 = 0x0060;
            TIMER_INSTANCE(BASE)->CCER = 0x0001;
        } else if constexpr (CHAN == 2) {
            TIMER_INSTANCE(BASE)->CCMR1 = 0x6000;
            TIMER_INSTANCE(BASE)->CCER = 0x0010;
        } else if constexpr (CHAN == 3) {
            TIMER_INSTANCE(BASE)->CCMR2 = 0x0060;
            TIMER_INSTANCE(BASE)->CCER = 0x0100;    
        } else if constexpr (CHAN == 4) {
            TIMER_INSTANCE(BASE)->CCMR2 = 0x6000;
            TIMER_INSTANCE(BASE)->CCER = 0x1000;
        }
    }
};

template <uint8_t CHAN>
using Timer2PWM = PWMTimer<TIM2_BASE, rcc::APB1Bus<RCC_APB1ENR_TIM2EN>, CHAN>;

template <uint8_t CHAN>
using Timer3PWM = PWMTimer<TIM3_BASE, rcc::APB1Bus<RCC_APB1ENR_TIM3EN>, CHAN>;

template <uint8_t CHAN>
using Timer4PWM = PWMTimer<TIM4_BASE, rcc::APB1Bus<RCC_APB1ENR_TIM4EN>, CHAN>;

template <uint8_t CHAN>
using Timer5PWM = PWMTimer<TIM5_BASE, rcc::APB1Bus<RCC_APB1ENR_TIM5EN>, CHAN>;

}




#endif
